import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Configure paths
const REPO_ROOT = path.resolve(__dirname, "..", "..");
const MARKDOWN_DIR = path.join(REPO_ROOT, "docs");
const MAPPING_FILE = path.join(__dirname, "social-sdk-mappings.json");

// Helper function to ensure paths are resolved correctly
const resolvePath = (relativePath) => {
  // If it's an absolute path, try it directly
  if (path.isAbsolute(relativePath) && fs.existsSync(relativePath)) {
    return relativePath;
  }

  // First try relative to current directory
  const currentPath = path.resolve(relativePath);
  if (fs.existsSync(currentPath)) {
    return currentPath;
  }

  // Then try relative to repo root
  const repoPath = path.join(REPO_ROOT, relativePath);
  if (fs.existsSync(repoPath)) {
    return repoPath;
  }

  throw new Error(`Could not resolve path: ${relativePath} (tried ${currentPath} and ${repoPath})`);
};

// Load the function/class mapping
const loadMapping = () => {
  const mappingPath = resolvePath(MAPPING_FILE);
  if (!fs.existsSync(mappingPath)) {
    // Mapping file ${mappingPath} not found!
    return {};
  }
  return JSON.parse(fs.readFileSync(mappingPath, "utf-8"));
};

// Function to find Markdown files recursively
const findMarkdownFiles = (dir) => {
  const resolvedDir = resolvePath(dir);

  let results = [];
  const files = fs.readdirSync(resolvedDir);

  for (const file of files) {
    const filePath = path.join(resolvedDir, file);
    const stat = fs.statSync(filePath);

    if (stat.isDirectory()) {
      results = results.concat(findMarkdownFiles(filePath));
    } else if (file.endsWith(".md") || file.endsWith(".mdx")) {
      results.push(filePath);
    }
  }
  return results;
};

// Generate reference markdown links while ignoring code blocks
const processMarkdownFile = (filePath, mapping) => {
  let content = fs.readFileSync(filePath, "utf-8");
  let lines = content.split("\n");

  // Find and remove existing auto-generated section
  const isMarkdown = filePath.endsWith(".md");
  const autoGenIndex = lines.findIndex((line) =>
    isMarkdown
      ? line.includes("<!-- Autogenerated Reference Links -->")
      : line.includes("{/* Autogenerated Reference Links */}"),
  );

  if (autoGenIndex !== -1) {
    lines = lines.slice(0, autoGenIndex);
  }

  let insideCodeBlock = false;
  // Use Map instead of Set to ensure uniqueness by short symbol
  let referencesFound = new Map();

  lines.forEach((line) => {
    if (line.trim().startsWith("```")) {
      insideCodeBlock = !insideCodeBlock;
      return;
    }

    if (!insideCodeBlock) {
      const regex = /\[`([A-Za-z0-9]+(?:::[A-Za-z0-9]+)?)`\](?!\()/g;
      let match;

      while ((match = regex.exec(line)) !== null) {
        const shortSymbol = match[1];
        const fullSymbol = `discordpp::${shortSymbol}`;

        if (mapping[fullSymbol] && !referencesFound.has(shortSymbol)) {
          referencesFound.set(shortSymbol, {
            short: shortSymbol,
            full: fullSymbol,
            url: mapping[fullSymbol],
          });
        } else if (!shortSymbol.includes("::")) {
          const constructorSymbol = `discordpp::${shortSymbol}::${shortSymbol}`;
          if (mapping[constructorSymbol] && !referencesFound.has(shortSymbol)) {
            referencesFound.set(shortSymbol, {
              short: shortSymbol,
              full: constructorSymbol,
              url: mapping[constructorSymbol],
            });
          }
        }
      }
    }
  });

  if (referencesFound.size > 0) {
    if (lines[lines.length - 1] !== "") {
      lines.push("");
    }

    lines.push(isMarkdown ? "<!-- Autogenerated Reference Links -->" : "{/* Autogenerated Reference Links */}");

    // Convert Map values to array and sort for consistent output
    const sortedRefs = Array.from(referencesFound.values()).sort((a, b) => a.short.localeCompare(b.short));

    for (const ref of sortedRefs) {
      lines.push(`[\`${ref.short}\`]: ${ref.url}`);
    }
  }

  const newContent = lines.join("\n");
  const hasChanged = newContent !== content;

  if (hasChanged) {
    fs.writeFileSync(filePath, newContent, "utf-8");
    // eslint-disable-next-line no-undef
    console.log(`Updated ${filePath} with ${referencesFound.size} references`);
  }

  return {
    updated: hasChanged,
    referenceCount: referencesFound.size,
  };
};

// Process all markdown files
const processMarkdownFiles = async () => {
  const mapping = loadMapping();
  if (!Object.keys(mapping).length) {
    // eslint-disable-next-line no-undef
    console.error("Mapping is empty! Make sure the JSON is generated correctly.");
    return;
  }

  const markdownFiles = findMarkdownFiles(MARKDOWN_DIR);
  let filesUpdated = 0;
  let totalReferences = 0;

  for (const filePath of markdownFiles) {
    const content = fs.readFileSync(filePath, "utf-8");
    const potentialMatches = content.match(/\[`([A-Za-z0-9]+(?:::[A-Za-z0-9]+)?)`\](?!\()/g);

    if (potentialMatches) {
      const { updated, referenceCount } = processMarkdownFile(filePath, mapping);
      if (updated) {
        filesUpdated++;
        totalReferences += referenceCount;
      }
    }
  }

  if (filesUpdated > 0) {
    // eslint-disable-next-line no-undef
    console.log(
      `\nSDK reference links processed. Updated ${filesUpdated} files with ${totalReferences} total references.`,
    );
  } else {
    // eslint-disable-next-line no-undef
    console.log("SDK reference links processed. No changes were made to any files.");
  }
};

// Run the script
processMarkdownFiles();
